package com.ndp.fb.fbagent.service.impl;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.ndp.fb.fbagent.service.impl.paramadapter.FacebookParamAdapter;
import com.ndp.fb.fbagent.service.webrequest.AccountQueueWebRequestor;
import com.ndp.fb.fbagent.service.webrequest.SyncNoAccountQueueWebRequestor;
import com.ndp.fb.model.fbagent.param.FacebookParam;
import com.ndp.fb.model.fbagent.result.FacebookResult;
import com.restfb.BinaryAttachment;
import com.restfb.Connection;
import com.restfb.DefaultFacebookClient;
import com.restfb.DefaultJsonMapper;
import com.restfb.FacebookClient;
import com.restfb.JsonMapper;
import com.restfb.Parameter;
import com.restfb.Version;
import com.restfb.exception.FacebookJsonMappingException;
import com.restfb.json.JsonException;
import com.restfb.json.JsonObject;
import com.restfb.types.Summary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by infi on 2015/1/21.
 */
public abstract class AbstractFBagentService {
    /**
     * 请求fb使用的版本
     */
    public static final Version VERSION = Version.Version_2_6;
    FacebookParamAdapter facebookParamAdapter = new FacebookParamAdapter();
    private Logger logger = LoggerFactory.getLogger(AbstractFBagentService.class);
    private JsonMapper jsonMapper = new DefaultJsonMapper();
    @Autowired
    private AccountQueueWebRequestor accountQueueWebRequestor;

    @Autowired
    private SyncNoAccountQueueWebRequestor defaultWebRequestor;

    /**
     * 组装参数
     *
     * @param param
     * @return
     */
    protected List<Parameter> adapter(FacebookParam param) {
        return facebookParamAdapter.adapterParam(param);
    }
    /**
     * 组装参数
     *
     * @param param
     * @return
     */
    protected List<Parameter> adapter(FacebookParam param,boolean isAdapterAttachmentParam) {
        return facebookParamAdapter.adapterParam(param, isAdapterAttachmentParam);
    }

    /**
     * 组装参数,返回数组
     *
     * @param param
     * @return
     */
    protected Parameter[] adapterArray(FacebookParam param) {
        List<Parameter> parameters = this.adapter(param);
        return parameters.toArray(new Parameter[parameters.size()]);
    }

    /**
     * 获取DefaultFacebookClient，异步
     *
     * @param accessToken
     * @return
     */
    protected DefaultFacebookClient getFacebookClient(String accessToken) {
        return new DefaultFacebookClient(accessToken, accountQueueWebRequestor, new DefaultJsonMapper(), VERSION);
    }

    /**
     * 临时使用，兼容2.4 CPC 
     * @param accessToken
     * @return
     */
    protected DefaultFacebookClient getFacebookClient24(String accessToken) {
        //// TODO: 2015/12/9  refactor ,assign to jacky
        return new DefaultFacebookClient(accessToken, accountQueueWebRequestor, new DefaultJsonMapper(), Version.Version_2_4);
    }


    /**
     * 获取DefaultFacebookClient，这样能统一
     *
     * @param accessToken
     * @return
     */
    protected DefaultFacebookClient getSyncFacebookClient(String accessToken) {
        return new DefaultFacebookClient(accessToken, defaultWebRequestor, new DefaultJsonMapper(), VERSION);
    }

    /**
     * 请求fb
     *
     * @param endpoint
     * @param accessToken
     * @param parameters
     * @return
     */
    public String makeGetRequest(String endpoint, String accessToken, Parameter... parameters) {
        DefaultFacebookClient facebookClient = getFacebookClient(accessToken);
        return facebookClient.makeRequest(endpoint, false, false, null, parameters);
    }

    /**
     * 请求fb
     *
     * @param endpoint
     * @param accessToken
     * @param parameters
     * @return
     */
    public String makePostRequest(String endpoint, String accessToken, Parameter... parameters) {
        DefaultFacebookClient facebookClient = getFacebookClient(accessToken);
        return facebookClient.makeRequest(endpoint, true, false, null, parameters);
    }


    /**
     * 带附件的请求
     *
     * @param endpoint
     * @param accessToken
     * @param binaryAttachments
     * @param parameters
     * @return
     */
    public String makePostRequest(String endpoint, String accessToken, List<BinaryAttachment> binaryAttachments, Parameter... parameters) {
        DefaultFacebookClient facebookClient = getFacebookClient(accessToken);
        return facebookClient.makeRequest(endpoint, true, false, binaryAttachments, parameters);
    }

    /**
     * 去fb删除对象
     * @param endpoint
     * @param accessToken
     * @param parameters
     * @return
     */
    public void deleteObject(String endpoint, String accessToken, Parameter... parameters)
    {
        DefaultFacebookClient facebookClient = getFacebookClient(accessToken);
        facebookClient.deleteObject(endpoint, parameters);
    }

    /**
     * 删除请求
     * @param endpoint
     * @param accessToken
     * @param parameters
     * @return
     */
    public String makeDeleteRequest(String endpoint, String accessToken, Parameter... parameters) {
        DefaultFacebookClient facebookClient = getFacebookClient(accessToken);
        return facebookClient.makeRequest(endpoint, false, true, null, parameters);
    }

    /**
     * 解析带附件的参数
     *
     * @param param
     * @return
     */
    protected Map adapterMap(FacebookParam param) {
        Map map = new HashMap();
        List params = this.adapter(param);
        if (CollectionUtils.isNotEmpty(params)) {
            List parameters = new ArrayList();
            List binaryAttachments = new ArrayList();
            for (Object object : params) {
                if (object instanceof Parameter) {
                    parameters.add(object);
                } else if (object instanceof BinaryAttachment) {
                    binaryAttachments.add(object);
                }
            }
            map.put("parameters", parameters.toArray(new Parameter[parameters.size()]));
            map.put("binaryAttachments", binaryAttachments);
        }
        return map;
    }

    /**
     * 次请求需要入队列
     *
     * @param uri
     * @param accessToken
     * @param parameterList
     * @param isAllData
     * @param offset
     * @param limit
     * @param resultClass
     * @param <T>
     * @return
     */
    public <T> FacebookResult fetchFacebookResult(String uri, String accessToken, List<Parameter> parameterList,
                                                  boolean isAllData, int offset, int limit, Class<T> resultClass) {
        String pattern= "after=([\\w]*)";
        Pattern p = Pattern.compile(pattern, 2 | Pattern.DOTALL);
        FacebookClient facebookClient = getFacebookClient(accessToken);
        List<T> returnList = new ArrayList<>();
        Summary summary;
        while (true) {
            logger.debug("offset >>>> " + offset);
            logger.debug("limit >>>> " + limit);
            int paramSize = parameterList.size();

            if (limit < 0) {
                Parameter[] parameters = new Parameter[paramSize + 1];
                System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                parameters[paramSize] = Parameter.with("offset", offset);
                Connection<T> connection = facebookClient.fetchConnection(uri, resultClass, parameters);
                summary = connection.getSummary();
                List tempList = connection.getData();
                returnList.addAll(tempList);
                //connection.hasNext() hashNext判断方法不准确
                if (tempList.size() == limit && isAllData) {
                    offset = offset + tempList.size();
                } else {
                    break;
                }
            } else {
                Parameter[] parameters = new Parameter[paramSize + 2];
                System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                parameters[paramSize] = Parameter.with("offset", offset);
                parameters[paramSize + 1] = Parameter.with("limit", limit);
                Connection<T> connection = facebookClient.fetchConnection(uri, resultClass, parameters);
                summary = connection.getSummary();
                List tempList = connection.getData();
                returnList.addAll(tempList);
                //connection.hasNext() hashNext判断方法不准确
                if (tempList.size() == limit && isAllData) {
                    offset = offset + tempList.size();
                } else {
                    break;
                }
            }
        }
        FacebookResult result = new FacebookResult();
        result.setObjects(returnList);
        result.setSummary(summary);
        return result;
    }

    public <T> T syncFetchObjectResult(String uri, String accessToken, Class<T> t, Parameter[] parameters) {

        FacebookClient facebookClient = getSyncFacebookClient(accessToken);
        String body = facebookClient.fetchObject(uri, String.class, parameters);
        JsonObject jsonObject = null;
        try {
            jsonObject = new JsonObject(body);
        } catch (JsonException e) {
            throw new FacebookJsonMappingException("The connection JSON you provided was invalid: " + jsonObject, e);
        }

        if (jsonObject != null) {
            return jsonMapper.toJavaObject(jsonObject.toString(), t);
        } else {
            return null;
        }

    }

    /**
     * 为了统一捕获异常
     *
     * @param uri
     * @param accessToken
     * @param t
     * @param parameters
     * @param <T>
     * @return
     */
    public <T> List<T> syncFetchObjectResultArray(String uri, String accessToken, Class<T> t, Parameter[] parameters) {
        FacebookClient facebookClient = getSyncFacebookClient(accessToken);
        String body = facebookClient.fetchObject(uri, String.class, parameters);
        return jsonMapper.toJavaList(body, t);
    }

    protected <T> List<T> syncFetchList(String uri, String accessToken, List<Parameter> parameterList,
                                        boolean isAllData, int offset, int limit, Class<T> resultClass) {
        FacebookClient facebookClient = getSyncFacebookClient(accessToken);
        String pattern= "after=([\\w]*)";
        Pattern p = Pattern.compile(pattern, 2 | Pattern.DOTALL);
        List<T> returnList = new ArrayList<>();
        Parameter after = null;
        while (true) {
            logger.debug("offset >>>> " + offset);
            logger.debug("limit >>>> " + limit);
            int paramSize = parameterList.size();

            if (limit < 0) {

                Parameter[] parameters = null;
                if(after == null){
                    parameters = new Parameter[paramSize + 1];
                    System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                    parameters[paramSize] = Parameter.with("offset", offset);
                } else {
                    parameters = new Parameter[paramSize + 2];
                    System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                    parameters[paramSize] = Parameter.with("offset", offset);
                    parameters[paramSize + 1] = after;
                }
                Connection<T> connection = facebookClient.fetchConnection(uri, resultClass, parameters);
                List tempList = connection.getData();
                returnList.addAll(tempList);
                //connection.hasNext() hashNext判断方法不准确
                if (connection.hasNext() && isAllData) {
                    offset = offset + tempList.size();
                    Matcher m = p.matcher(connection.getNextPageUrl());
                    if(m.find()) {
                        after = Parameter.with("after", m.group(1));
                    } else {
                        after = null;
                    }
                } else {
                    break;
                }
            } else {
                Parameter[] parameters = null;
                if(after == null){
                    parameters = new Parameter[paramSize + 2];
                    System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                    parameters[paramSize] = Parameter.with("offset", offset);
                    parameters[paramSize + 1] = Parameter.with("limit", limit);
                } else {
                    parameters = new Parameter[paramSize + 3];
                    System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                    parameters[paramSize] = Parameter.with("offset", offset);
                    parameters[paramSize + 1] = Parameter.with("limit", limit);
                    parameters[paramSize + 2] = after;
                }
                Connection<T> connection = facebookClient.fetchConnection(uri, resultClass, parameters);
                List tempList = connection.getData();
                returnList.addAll(tempList);
                //connection.hasNext() hashNext判断方法不准确
                if (connection.hasNext() && isAllData) {
                    offset = offset + tempList.size();

                    Matcher m = p.matcher(connection.getNextPageUrl());
                    if(m.find()) {
                        after = Parameter.with("after", m.group(1));
                    } else {
                        after = null;
                    }
                } else {
                    break;
                }
            }
        }
        return returnList;
    }

    protected <T> FacebookResult<T> syncFetchFacebookResult(String uri, String accessToken, List<Parameter> parameterList,
                                                            boolean isAllData, int offset, int limit, Class<T> resultClass) {
        FacebookClient facebookClient = getSyncFacebookClient(accessToken);
        String pattern= "after=([\\w]*)";
        Pattern p = Pattern.compile(pattern, 2 | Pattern.DOTALL);
        List<T> returnList = new ArrayList<>();
        Summary summary;
        Parameter after = null;
        while (true) {
            logger.debug("offset >>>> " + offset);
            logger.debug("limit >>>> " + limit);
            int paramSize = parameterList.size();
            if (limit < 0) {

                Parameter[] parameters = null;
                if(after == null){
                    parameters = new Parameter[paramSize + 1];
                    System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                    parameters[paramSize] = Parameter.with("offset", offset);
                } else {
                    parameters = new Parameter[paramSize + 1];
                    System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                    parameters[paramSize] = Parameter.with("offset", offset);
                    parameters[paramSize + 1] = after;
                }
                Connection<T> connection = facebookClient.fetchConnection(uri, resultClass, parameters);
                summary = connection.getSummary();
                List tempList = connection.getData();
                returnList.addAll(tempList);
//                connection.hasNext() hashNext判断方法不准确
                if (connection.hasNext() && isAllData) {
                    offset = offset + tempList.size();
                    Matcher m = p.matcher(connection.getNextPageUrl());
                    if(m.find()) {
                        after = Parameter.with("after", m.group(1));
                    } else {
                        after = null;
                    }
                } else {
                    break;
                }
            } else {
                Parameter[] parameters = null;
                if(after == null){
                     parameters = new Parameter[paramSize + 2];
                    System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                    parameters[paramSize] = Parameter.with("offset", offset);
                    parameters[paramSize + 1] = Parameter.with("limit", limit);
                } else {
                    parameters = new Parameter[paramSize + 3];
                    System.arraycopy(parameterList.toArray(), 0, parameters, 0, paramSize);
                    parameters[paramSize] = Parameter.with("offset", offset);
                    parameters[paramSize + 1] = Parameter.with("limit", limit);
                    parameters[paramSize + 2] = after;
                }
                Connection<T> connection = facebookClient.fetchConnection(uri, resultClass, parameters);
                summary = connection.getSummary();
                List tempList = connection.getData();
                returnList.addAll(tempList);
                //connection.hasNext() hashNext判断方法不准确
                if (connection.hasNext() && isAllData) {
                    offset = offset + tempList.size();
                    Matcher m = p.matcher(connection.getNextPageUrl());
                    if(m.find()) {
                        after = Parameter.with("after",m.group(1));
                    } else {
                        after = null;
                    }
                } else {
                    break;
                }
            }
        }
        FacebookResult result = new FacebookResult();
        result.setObjects(returnList);
        result.setSummary(summary);
        return result;
    }

    protected <T> List<T> syncFetchList(String uri, String accessToken, List<Parameter> parameterList,
                                        boolean isAllData, int offset, Class<T> resultClass) {
        return syncFetchList(uri, accessToken, parameterList, isAllData, offset, -1, resultClass);
    }

    protected <T> List<T> syncFetchList(String uri, String accessToken, List<Parameter> parameterList,
                                        boolean isAllData, Class<T> resultClass) {
        return syncFetchList(uri, accessToken, parameterList, isAllData, 0, -1, resultClass);
    }

    public FacebookClient.AccessToken getAccessToken(String uri, Parameter[] paramters) {
        DefaultFacebookClient facebookClient = new DefaultFacebookClient();
        String response = facebookClient.makeRequest(uri, false, false, null, paramters);
        return FacebookClient.AccessToken.fromQueryString(response);
    }

    /**
     * 请求fb
     *
     * @param endpoint
     * @param accessToken
     * @param parameters
     * @return
     */
    public String syncMakePostRequest(String endpoint, String accessToken, Parameter... parameters) {
        DefaultFacebookClient facebookClient = getSyncFacebookClient(accessToken);
        return facebookClient.makeRequest(endpoint, true, false, null, parameters);
    }
}
